package ac.you.api.command;

import ac.you.api.AcYouAPI;
import ac.you.api.command.t.LimitCombined;
import ac.you.api.command.t.LimitParam;
import ac.you.api.tl.AcArray;
import ac.you.api.tl.AcString;
import com.sun.istack.internal.NotNull;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;

import java.util.*;
import java.util.function.Consumer;
import java.util.logging.Level;

public final class Command implements TabExecutor {
    private static final String A = "Command";

    private int u = 0;

    private final String mainCommand;

    private String permission;

    private final Execute defaulExecute;

    private CommandType cT;

    private boolean asyn;

    private List<LimitCombined> limitCombineds;

    private List<LimitParam> limitParams;

    private Consumer<Package$PAS> help;

    public static Command main(String command){
        return new Command(command);
    }

    public Command(@NotNull String mainCommand, String permission , CommandType commandType, Consumer<Package$EAP> error, Consumer<Package$PAS> help , Consumer<Package$PAS> defaultPAS, List<LimitParam> limitParams,List<LimitCombined> limitCombineds, boolean asyn) {
        this.mainCommand = mainCommand;
        this.permission = permission;
        this.asyn = asyn;
        this.cT = commandType;
        this.help = help;
        this.limitParams = limitParams;
        this.limitCombineds = limitCombineds;
        this.defaulExecute = new Execute(defaultPAS, error);
    }

    public Command(String maincommand, String permission , boolean asyn){
        this(maincommand,permission,CommandType.PLAYER,null,null,null,null,null,asyn);
    }
    public Command(String mainCommand){
        this(mainCommand,null,false);
    }
    public void limit(LimitParam... limitParam){
        if (limitParams == null) {
            limitParams = new ArrayList<>();
        }
        for (LimitParam param : limitParam) {
            u++;
            limitParams.add(param);
        }

    }
    public Command limit(String[] limitparam,String[] ali){
        if (limitparam.length > 0){
            if (ali != null) {
                limit(LimitParam.build(limitparam,ali));
            }else {
                limit(LimitParam.build(limitparam));
            }
        }
        return this;
    }
    public Command combinedExecute(LimitCombined... limitCombineds){
        if (this.limitCombineds == null){
            this.limitCombineds = new ArrayList<>();
        }
        if (limitCombineds.length > 0){
            this.limitCombineds.addAll(Arrays.asList(limitCombineds));
        }
        return this;
    }
    public Command combinedExecute(String permission, Consumer<Package$PAS> f, Consumer<Package$EAP> f1, boolean asyn, int... limit){
        return combinedExecute(new LimitCombined(limit, permission, new Execute(f, f1), asyn));
    }
    public Command defaultExecute(Consumer<Package$PAS> f , Consumer<Package$EAP> f1, boolean asyn){
        this.defaulExecute.f = f;
        this.defaulExecute.f1 = f1;
        this.asyn = asyn;
        return this;
    }
    public Command help(Consumer<Package$PAS> f){
        this.help = f;
        return this;
    }
    public Command permission(String permission){
        this.permission = permission;
        return this;
    }
    public Command asyn(boolean i){
        this.asyn = i;
        return this;
    }
    public Command only(CommandType commandType){
        this.cT = commandType;
        return this;
    }
    public void register() throws Exception {
        if (limitCombineds.size() > 0 && limitParams.size() == 0) throw new CannotMatchCombinationException("Unable to find the restricted parameter list for registration");
        Bukkit.getPluginCommand(mainCommand).setExecutor(this);
        AcYouAPI.log(Level.INFO,"Successfully registered command : " + mainCommand + "!");
    }

    @Override
    public boolean onCommand(CommandSender commandSender, org.bukkit.command.Command command, String s, String[] strings) {
        if (s.equals(mainCommand) && strings.length > 0){
            // help
            if (strings[0].equals("help") && help != null){
                help.accept(new Package$PAS(strings,commandSender));
                return true;
            }
            if (u > 0 && strings.length < u){
                AcYouAPI.log(Level.WARNING,"Restricted parameters do not correspond!");
                if (defaulExecute != null) defaulExecute.f1.accept(new Package$EAP(new WrongCombinationIndexException("Restricted parameters do not correspond!"),commandSender));
                return false;
            }
            //1. 匹配组合参数
            LimitCombined var1 = null;
            if (limitCombineds != null && limitCombineds.size() > 0 && limitParams != null && !limitParams.isEmpty()){
                for (LimitCombined limitCombined : limitCombineds) {
                    int[] lt = limitCombined.limit();
                    for (int i = 0; i < lt.length; i++) {
                        String s1 = strings[i];
                        LimitParam lp = limitParams.get(i);
                        if (!lp.match(s1,lt[i])) break;
                        if (i == lt.length - 1) {
                            var1 = limitCombined;
                        }
                    }
                    if (var1 != null) break;
                }
            } else {
                if (defaulExecute != null){
                    if (asyn){
                        Bukkit.getScheduler().runTaskAsynchronously(AcYouAPI.INSTANCE,() -> defaulExecute.f.accept(new Package$PAS(AcArray.cutFirst(strings, u),commandSender)));
                    } else {
                        defaulExecute.f.accept(new Package$PAS(AcArray.cutFirst(strings, u),commandSender));
                    }
                }
                return true;
            }
            if (var1 == null){
                AcYouAPI.log(Level.WARNING,"Cannot find the combination parameters for this command!");
                if (defaulExecute != null){
                    defaulExecute.f.accept(new Package$PAS(AcArray.cutFirst(strings,u),commandSender));
                    defaulExecute.f1.accept(new Package$EAP(new WrongCombinationException("Cannot find the combination parameters for this command!"),commandSender));
                }
                return false;
            }
            //2. 检查权限
            if (checkPermission(commandSender,var1.permission()) || checkPermission(commandSender,this.permission)){
                //3. 判断是否异步执行
                try {
                    if (var1.isAsyn()){
                        LimitCombined var2 = var1;
                        Bukkit.getScheduler().runTaskAsynchronously(AcYouAPI.INSTANCE,() -> var2.execute().f.accept(new Package$PAS(AcArray.cutFirst(strings, u),commandSender)));
                    } else {
                        var1.execute().f.accept(new Package$PAS(AcArray.cutFirst(strings, u),commandSender));
                    }
                    AcYouAPI.log(Level.INFO,"Successfully executed command!");
                } catch (Exception e){
                    AcYouAPI.log(Level.WARNING,e.getMessage());
                    var1.execute().f1.accept(new Package$EAP(e,commandSender));
                }
            }
            return true;
        }
        return false;
    }
    private boolean checkPermission(CommandSender commandSender,String permission){
        switch (cT){
            case PERMISSION:
                return AcString.isNull(permission) || commandSender.hasPermission(permission);
            case PLAYER:
                return ((Player) commandSender).isOnline();
            case OP:
                return commandSender.isOp();
            case CONSOLE:
                return commandSender instanceof ConsoleCommandSender;
        }
        return false;
    }

    @Override
    public List<String> onTabComplete(CommandSender commandSender, org.bukkit.command.Command command, String s, String[] strings) {
        return null;
    }

    public enum CommandType{
        CONSOLE,
        PERMISSION,
        OP,
        PLAYER
    }
    public static final class Package$PAS {
        private final String[] a;
        private final CommandSender p;
        public Package$PAS(String[] a, CommandSender p) { this.a = a;this.p = p; }
        public String[] params() { return a; }
        public CommandSender player() { return p; }
    }
    public static final class Package$EAP {
        private final Exception a;
        private final CommandSender p;
        public Package$EAP(Exception a, CommandSender p) { this.a = a;this.p = p; }
        public Exception exception() { return a; }
        public CommandSender player() { return p; }
    }
    static class WrongCombinationException extends Exception{
        public WrongCombinationException(String message) {
            super(message);
        }
    }
    static class WrongCombinationIndexException extends Exception{
        public WrongCombinationIndexException(String message) {
            super(message);
        }
    }
    static class CannotMatchCombinationException extends Exception{
        public CannotMatchCombinationException(String message) {
            super(message);
        }
    }
    public static final class Execute {
        private  Consumer<Command.Package$PAS> f;
        private  Consumer<Command.Package$EAP> f1;
        public Execute(Consumer<Package$PAS> f, Consumer<Package$EAP> f1) {
            this.f = f;
            this.f1 = f1;
        }
        public Consumer<Package$PAS> gpas() {
            return f;
        }
        public Consumer<Package$EAP> geap() {
            return f1;
        }
    }
}
